package com.william.jdbcplus.core.conditions.wrap;

import com.google.common.collect.Lists;
import com.william.jdbcplus.core.conditions.lambda.*;
import com.william.jdbcplus.core.conditions.segment.MergeSegments;
import com.william.jdbcplus.core.enums.SqlAggregateKeyword;
import com.william.jdbcplus.core.enums.SqlKeyword;
import com.william.jdbcplus.core.enums.SqlLike;
import com.william.jdbcplus.core.metadata.TableHelperUtil;
import com.william.jdbcplus.core.toolkit.*;
import com.william.jdbcplus.core.toolkit.lambda.LambdaUtils;
import com.william.jdbcplus.core.toolkit.lambda.support.SFunction;
import com.william.jdbcplus.core.toolkit.sql.SqlUtils;
import lombok.Getter;
import lombok.Setter;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.joining;

/**
 * 通用查询sql封装
 *
 * @author niliwei
 */
@Getter
@Setter
public abstract class AbstractLambdaWrap<Wrap extends AbstractLambdaWrap<Wrap>> implements Select<Wrap>, From<Wrap>, Where<Wrap>, GroupBy<Wrap>, Having<Wrap>, OrderBy<Wrap>, Union<Wrap>, IWrap {

    /**
     * 占位符
     */
    protected final Wrap typedThis = (Wrap) this;

    private final int maxInSet = 1000;

    protected Map<String, Object> paramNameValuePairs;

    protected MergeSegments expression;

    /**
     * 子类返回一个自己的新对象
     */
    protected abstract Wrap instance();

    public MergeSegments getExpression() {
        return expression;
    }

    @Override
    public Map<String, Object> getParamNameValuePairs() {
        return paramNameValuePairs;
    }

    /**
     * 对sql片段进行组装
     *
     * @param sqlSegments sql片段数组
     * @return children
     */
    protected Wrap doIt(SqlKeyword keyword, String... sqlSegments) {
        //全部以空格分隔
        String segment = Arrays.stream(sqlSegments).collect(joining(StringPool.SPACE));
        expression.add(keyword, segment);
        return typedThis;
    }

    /**
     * 获取 columnName
     */
    public String columnToString(SFunction column) {
        String fieldName = LambdaUtils.getFieldProperty(column);
        Class<?> clazz = LambdaUtils.getFuncClass(column);
        String tableName = TableHelperUtil.getTableName(clazz);
        String colunmString = TableHelperUtil.getEntityFieldColumn(clazz, fieldName);
        return tableName + StringPool.DOT + colunmString;
    }

    /**
     * 选取一列
     */
    public String selectToString(SFunction column) {
        String fieldName = LambdaUtils.getFieldProperty(column);
        Class<?> clazz = LambdaUtils.getFuncClass(column);
        String tableName = TableHelperUtil.getTableName(clazz);
        String colunmString = TableHelperUtil.getEntityFieldColumn(clazz, fieldName);
        return tableName + StringPool.DOT + colunmString + SQLKeyUtils.asFieldColumn(fieldName, colunmString);
    }

    /**
     * 选取多列
     *
     * @param columns
     * @return
     */
    public String selectsToString(SFunction... columns) {
        return Arrays.stream(columns).map(this::selectToString).collect(joining(StringPool.COMMA));
    }

    /**
     * 获取 as fieldName
     */
    public String columnAsToFieldName(SFunction column) {
        String fieldName = LambdaUtils.getFieldProperty(column);
        return fieldName + StringPool.UNDERSCORE;
    }

    /**
     * 获取 tableName
     */
    protected String tableToString(String table) {
        return table;
    }

    /**
     * 获取 tableName
     */
    public String tableToString(Class<?> clazz) {
        String tableName = TableHelperUtil.getTableName(clazz);
        return tableName;
    }

    /**
     * 多字段转换为逗号 "," 分割字符串
     *
     * @param columns 多字段
     */
    public String columnsToString(SFunction... columns) {
        return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA));
    }

    /**
     * 格式化SQL
     *
     * @param sqlStr SQL语句部分
     * @param params 参数集
     * @return sql
     */
    protected final String formatSql(String sqlStr, Object... params) {
        return formatSqlIfNeed(sqlStr, params);
    }

    /**
     * 格式化SQL
     *
     * @param param 参数集
     * @return sql
     */
    protected final String formatSql(Object param) {
        String sqlStr = "{0}";
        return formatSqlIfNeed(sqlStr, param);
    }

    /**
     * <p>
     * 根据需要格式化SQL<br>
     * <br>
     * Format SQL for methods: EntityQ<T>.where/and/or...("name={0}", value);
     * ALL the {<b>i</b>} will be replaced with #{MPGENVAL<b>i</b>}<br>
     * <br>
     * ew.where("sample_name=<b>{0}</b>", "haha").and("sample_age &gt;<b>{0}</b>
     * and sample_age&lt;<b>{1}</b>", 18, 30) <b>TO</b>
     * sample_name=<b>#{MPGENVAL1}</b> and sample_age&gt;#<b>{MPGENVAL2}</b> and
     * sample_age&lt;<b>#{MPGENVAL3}</b><br>
     * </p>
     *
     * @param sqlStr SQL语句部分
     * @param params 参数集
     * @return sql
     */
    protected final String formatSqlIfNeed(String sqlStr, Object... params) {
        if (StringUtil.isEmpty(sqlStr)) {
            return null;
        }
        if (ArrayUtils.isNotEmpty(params)) {
            for (int i = 0; i < params.length; ++i) {
                String genParamName = Constants.WRAPPER_PARAM + "_" + UuidUtil.get32UUID();
                sqlStr = sqlStr.replace(String.format("{%s}", i), StringPool.COLON + genParamName);
                Object param = params[i];
                if (param != null) {
                    if (param instanceof Boolean) {
                        Boolean value = (Boolean) param;
                        param = value ? Integer.valueOf(1) : Integer.valueOf(0);
                    } else if (param instanceof BigInteger) {
                        param = param.toString();
                    }
                }
                paramNameValuePairs.put(genParamName, param);
            }
        }
        return sqlStr;
    }

    /**
     * 获取in表达式 包含括号
     *
     * @param value 集合
     */
    protected String inExpression(Collection<?> value) {
        return value.stream().map(i -> formatSql("{0}", i)).collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
    }

    /**
     * 必要的初始化
     */
    protected final void initNeed() {
        paramNameValuePairs = new HashMap<>(16);
        expression = new MergeSegments();
    }

    @Override
    @SuppressWarnings("all")
    public Wrap clone() {
        return SerializationUtils.clone(typedThis);
    }

    /**
     * 内部自用
     * <p>拼接 LIKE 以及 值</p>
     */
    protected Wrap likeValue(SFunction column, Object val, SqlKeyword like, SqlLike sqlLike) {
        String concatColumn = SqlKeyword.LOWER + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET;
        String concatLike = SqlKeyword.LOWER + StringPool.LEFT_BRACKET + formatSql("{0}", SqlUtils.concatLike(val, sqlLike)) + StringPool.RIGHT_BRACKET;
        return doIt(SqlKeyword.WHERE, concatColumn, like.toString(), concatLike);
    }

    /**
     * 普通查询条件
     *
     * @param column     属性
     * @param sqlKeyword SQL 关键词
     * @param val        条件值
     */
    protected Wrap addCondition(SFunction column, SqlKeyword sqlKeyword, Object val) {
        return doIt(SqlKeyword.WHERE, columnToString(column), sqlKeyword.toString(), formatSql("{0}", val));
    }

    /**
     * 子查询拼接
     *
     * @param column     属性
     * @param sqlKeyword SQL 关键词
     * @param func       条件值
     */
    protected Wrap addSubCondition(SFunction column, SqlKeyword sqlKeyword, Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        String sql = wrap.getAll();
        return doIt(SqlKeyword.WHERE, columnToString(column), sqlKeyword.toString(), String.format("(%s)", sql));
    }

    /**
     * 嵌套查询条件
     *
     * @param func 函数
     */
    protected Wrap addLinkCondition(Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        String sql = wrap.expression.getWhereSegment();
        return doIt(SqlKeyword.WHERE, String.format("(%s)", sql));
    }

    /**
     * 连接查询条件
     *
     * @param joinType 连接类型关键字
     * @param r1       左函数
     * @param r2       右函数
     */
    protected <R, R2> Wrap addJoinCondition(SqlKeyword joinType, Class<R> clazz, SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        String left = columnToString(r1);
        String tableLeft = StringUtil.split(left, StringPool.DOT)[0];
        String right = columnToString(r2);
        String tableRight = StringUtil.split(right, StringPool.DOT)[0];
        return doIt(SqlKeyword.FROM, joinType.toString(), tableLeft, SqlKeyword.ON.toString(), left, SqlKeyword.EQ.toString(), right);
    }

    /**
     * 添加having嵌套聚合函数
     *
     * @param keyword 关键字
     * @param func    函数
     */
    protected Wrap addHavingCondition(SqlAggregateKeyword keyword, Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        //获取 where 分段列表
        List<String> whereSqlList = wrap.expression.getWhereList();
        // 关键字集合
        Set<String> listKeyWork = Arrays.asList(SqlKeyword.values()).stream().map(e -> e.getSegment()).collect(Collectors.toSet());
        for (String sql : whereSqlList) {
            //sql 实例比如 and id > 2  改为  ---> and max(id) > 2
            // or id >= 3 改为 or count(id) >= 3
            String sqlSplitFirst = StringUtil.split(sql, StringPool.SPACE)[0];
            // 首字符串为关键字
            if (listKeyWork.contains(sqlSplitFirst)) {
                doIt(SqlKeyword.HAVING, sql);
                continue;
            }

            String column = sqlSplitFirst;
            String columnAggr = keyword.toString() + StringPool.LEFT_BRACKET + sqlSplitFirst + StringPool.RIGHT_BRACKET;
            String columnSql = sql.replaceFirst(column, columnAggr);
            doIt(SqlKeyword.HAVING, columnSql);
        }

        return typedThis;
    }

    /**
     * 函数转 wrap
     *
     * @param func
     * @return
     */
    protected Wrap getWrap(Function<Wrap, Wrap> func) {
        Wrap wrap = func.apply(instance());
        return wrap;
    }

    /**
     * 从列表中获取一个第一个对象
     *
     * @param list
     * @param <M>
     * @return
     */
    protected <M> M getFirst(List<M> list) {
        if (list == null || list.size() == 0) {
            return null;
        } else {
            return list.get(0);
        }
    }

    /**
     * jdbc脚本转换为mybatis查找脚本
     *
     * @param sql
     * @return
     */
    protected String toMybatisSql(String sql) {
        for (Map.Entry<String, Object> entry : paramNameValuePairs.entrySet()) {
            String genParamName = StringPool.COLON + entry.getKey();
            String sqlParam = String.format(Constants.WRAPPER_PARAM_FORMAT, Constants.WRAPPER, entry.getKey());
            sql = sql.replace(genParamName, sqlParam);
        }
        return sql;
    }

    /**
     * jdbc脚本转换为实际执行脚本
     *
     * @param sql
     * @return
     */
    protected String toExecSql(String sql) {
        for (Map.Entry<String, Object> entry : paramNameValuePairs.entrySet()) {
            String genParamName = StringPool.COLON + entry.getKey();
            Object paramValue = entry.getValue();
            String paramSql = StringPool.EMPTY;
            if (paramValue == null) {
                paramSql = "null";
            } else if (paramValue instanceof String) {
                paramSql = StringPool.SINGLE_QUOTE + paramValue.toString() + StringPool.SINGLE_QUOTE;
            } else if (paramValue instanceof Date) {
                paramSql = StringPool.SINGLE_QUOTE + DateUtil.formatDateTime((Date) paramValue) + StringPool.SINGLE_QUOTE;
            } else {
                paramSql = paramValue.toString();
            }
            sql = sql.replace(genParamName, paramSql);
        }
        return sql;
    }

    @Override
    public <R> Wrap select(String sql) {
        return doIt(SqlKeyword.SELECT, sql);
    }

    @Override
    public <R> Wrap select(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, selectToString(column));
    }

    @Override
    public <R> Wrap select(SFunction<R, ?>... columns) {
        return doIt(SqlKeyword.SELECT, columns.length == 1 ? selectToString(columns[0]) : selectsToString(columns));
    }

    @Override
    public <R, R2> Wrap selectAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, columnToString(column), SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public <R> Wrap selectMax(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.MAX.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET);
    }

    @Override
    public <R, R2> Wrap selectMaxAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.MAX.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public <R> Wrap selectMin(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.MIN.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET);
    }

    @Override
    public <R, R2> Wrap selectMinAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.MIN.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public <R> Wrap selectSum(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.SUM.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET);
    }

    @Override
    public <R, R2> Wrap selectSumAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.SUM.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public <R> Wrap selectAvg(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.AVG.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET);
    }

    @Override
    public <R, R2> Wrap selectAvgAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.AVG.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public <R> Wrap selectCount(SFunction<R, ?> column) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.COUNT.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET);
    }

    @Override
    public <R, R2> Wrap selectCountAs(SFunction<R, ?> column, SFunction<R2, ?> asColumn) {
        return doIt(SqlKeyword.SELECT, SqlKeyword.COUNT.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, SqlKeyword.AS.toString(), columnAsToFieldName(asColumn));
    }

    @Override
    public Wrap selectAll(Class<?> clazz) {
        String selectAllColumn = TableHelperUtil.getSelectAllColumn(clazz);
        return doIt(SqlKeyword.SELECT, selectAllColumn);
    }

    @Override
    public Wrap selectDistinct(boolean isDistinct) {
        expression.setDistinct(isDistinct);
        return typedThis;
    }

    @Override
    public Wrap from(Class<?> clazz) {
        return doIt(SqlKeyword.FROM, tableToString(clazz));
    }

    @Override
    public Wrap from(String table) {
        return doIt(SqlKeyword.FROM, tableToString(table));
    }

    @Override
    public <R, R2> Wrap join(Class<R> clazz, SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        return addJoinCondition(SqlKeyword.JOIN, clazz, r1, r2);
    }

    @Override
    public <R, R2> Wrap innerJoin(Class<R> clazz, SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        return addJoinCondition(SqlKeyword.INNER_JOIN, clazz, r1, r2);
    }

    @Override
    public <R, R2> Wrap leftJoin(Class<R> clazz, SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        return addJoinCondition(SqlKeyword.LEFT_JOIN, clazz, r1, r2);
    }

    @Override
    public <R, R2> Wrap rightJoin(Class<R> clazz, SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        return addJoinCondition(SqlKeyword.RIGHT_JOIN, clazz, r1, r2);
    }

    @Override
    public <R> Wrap groupBy(SFunction<R, ?> column) {
        return doIt(SqlKeyword.GROUP_BY, columnToString(column));
    }

    @Override
    public <R> Wrap groupBy(SFunction<R, ?>... columns) {
        if (ArrayUtils.isEmpty(columns)) {
            return typedThis;
        }
        return doIt(SqlKeyword.GROUP_BY, columns.length == 1 ? columnToString(columns[0]) : columnsToString(columns));
    }

    @Override
    public Wrap having(String sqlHaving, Object... params) {
        return doIt(SqlKeyword.HAVING, formatSqlIfNeed(sqlHaving, params));
    }

    @Override
    public Wrap having(Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        return doIt(SqlKeyword.HAVING, wrap.getWhereSegment());
    }

    @Override
    public Wrap havingCount(Function<Wrap, Wrap> func) {
        return addHavingCondition(SqlAggregateKeyword.COUNT, func);
    }

    @Override
    public Wrap havingMax(Function<Wrap, Wrap> func) {
        return addHavingCondition(SqlAggregateKeyword.MAX, func);
    }

    @Override
    public Wrap havingMin(Function<Wrap, Wrap> func) {
        return addHavingCondition(SqlAggregateKeyword.MIN, func);
    }

    @Override
    public Wrap havingAvg(Function<Wrap, Wrap> func) {
        return addHavingCondition(SqlAggregateKeyword.AVG, func);
    }

    @Override
    public Wrap havingSum(Function<Wrap, Wrap> func) {
        return addHavingCondition(SqlAggregateKeyword.SUM, func);
    }

    @Override
    public <R> Wrap orderBy(boolean isAsc, SFunction<R, ?>... columns) {
        if (ArrayUtils.isEmpty(columns)) {
            return typedThis;
        }
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        for (SFunction<R, ?> column : columns) {
            doIt(SqlKeyword.ORDER_BY, columnToString(column), mode.toString());
        }
        return typedThis;
    }

    @Override
    public <R> Wrap orderBy(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, columnToString(column), mode.toString());
    }

    @Override
    public <R> Wrap orderByCount(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, SqlKeyword.COUNT.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, mode.toString());
    }

    @Override
    public <R> Wrap orderBySum(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, SqlKeyword.SUM.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, mode.toString());
    }

    @Override
    public <R> Wrap orderByAvg(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, SqlKeyword.AVG.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, mode.toString());
    }

    @Override
    public <R> Wrap orderByMax(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, SqlKeyword.MAX.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, mode.toString());
    }

    @Override
    public <R> Wrap orderByMin(boolean isAsc, SFunction<R, ?> column) {
        SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
        return doIt(SqlKeyword.ORDER_BY, SqlKeyword.MIN.toString() + StringPool.LEFT_BRACKET + columnToString(column) + StringPool.RIGHT_BRACKET, mode.toString());
    }

    @Override
    public Wrap union(Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        String sql = wrap.expression.getUnion();
        return doIt(SqlKeyword.UNION, sql);
    }

    @Override
    public Wrap unionAll(Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        String sql = wrap.expression.getUnion();
        return doIt(SqlKeyword.UNION_All, sql);
    }

    /**
     * <p>拼接 NOT</p>
     */
    @Override
    public Wrap not() {
        return doIt(SqlKeyword.WHERE, SqlKeyword.NOT.toString());
    }

    /**
     * <p>拼接 AND</p>
     */
    @Override
    public Wrap and() {
        return doIt(SqlKeyword.WHERE, SqlKeyword.AND.toString());
    }

    /**
     * <p>拼接 OR</p>
     */
    @Override
    public Wrap or() {
        return doIt(SqlKeyword.WHERE, SqlKeyword.OR.toString());
    }

    @Override
    public <R> Wrap eq(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.EQ, val);
    }

    @Override
    public <R> Wrap eq(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.EQ, func);
    }

    @Override
    public <R> Wrap ne(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.NE, val);
    }

    @Override
    public <R> Wrap ne(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.NE, func);
    }

    @Override
    public <R> Wrap gt(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.GT, val);
    }

    @Override
    public <R> Wrap gt(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.GT, func);
    }

    @Override
    public <R> Wrap ge(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.GE, val);
    }

    @Override
    public <R> Wrap ge(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.GE, func);
    }

    @Override
    public <R> Wrap lt(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.LT, val);
    }

    @Override
    public <R> Wrap lt(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.LT, func);
    }

    @Override
    public <R> Wrap le(SFunction<R, ?> column, Object val) {
        return addCondition(column, SqlKeyword.LE, val);
    }

    @Override
    public <R> Wrap le(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.LE, func);
    }

    @Override
    public <R> Wrap between(SFunction<R, ?> column, Object val1, Object val2) {
        return doIt(SqlKeyword.WHERE, columnToString(column), SqlKeyword.BETWEEN.toString(), formatSql("{0}", val1), SqlKeyword.AND.toString(), formatSql("{0}", val2));
    }

    @Override
    public <R> Wrap notBetween(SFunction<R, ?> column, Object val1, Object val2) {
        return doIt(SqlKeyword.WHERE, columnToString(column), SqlKeyword.NOT.toString(), SqlKeyword.BETWEEN.toString(), formatSql("{0}", val1), SqlKeyword.AND.toString(), formatSql("{0}", val2));
    }

    @Override
    public <R> Wrap like(SFunction<R, ?> column, Object val) {
        return likeValue(column, val, SqlKeyword.LIKE, SqlLike.DEFAULT);
    }

    @Override
    public <R> Wrap notLike(SFunction<R, ?> column, Object val) {
        return likeValue(column, val, SqlKeyword.NOTLIKE, SqlLike.DEFAULT);
    }

    @Override
    public <R> Wrap likeLeft(SFunction<R, ?> column, Object val) {
        return likeValue(column, val, SqlKeyword.LIKE, SqlLike.LEFT);
    }

    @Override
    public <R> Wrap likeRight(SFunction<R, ?> column, Object val) {
        return likeValue(column, val, SqlKeyword.LIKE, SqlLike.RIGHT);
    }

    @Override
    public <R> Wrap in(SFunction<R, ?> column, Collection<?> collValues) {
        // 列表为空，条件直接为FALSE
        if (collValues == null || collValues.size() == 0) {
            Function<Wrap, Wrap> tempFalse = e -> e.connect("1 = 0");
            typedThis.and(tempFalse);
            return typedThis;
        }
        //列表只有一个对象，直接相等比较
        if (collValues.size() == 1) {
            List<Object> listValues = CollectionUtils.toListObj(collValues);
            typedThis.eq(column, listValues.get(0));
            return typedThis;
        }
        // 列表个数大于 1000
        if (collValues.size() > maxInSet) {
            List<Object> listValues = CollectionUtils.toListObj(collValues);
            List<List<Object>> listSet = Lists.partition(listValues, maxInSet);
            Function<Wrap, Wrap> result = null;
            for (List<Object> listObj : listSet) {
                Function<Wrap, Wrap> tempIn = e -> e.in(column, listObj);
                Function<Wrap, Wrap> tempOr = b -> b.or(tempIn);
                if (result == null) {
                    result = tempOr;
                } else {
                    result = result.andThen(tempOr);
                }
            }
            typedThis.and(result);
            return typedThis;
        } else {
            return doIt(SqlKeyword.WHERE, columnToString(column), SqlKeyword.IN.toString(), inExpression(collValues));
        }
    }

    /**
     * NOT IN (col1, col2...)
     *
     * @param column
     * @param collValues
     * @return
     */
    @Override
    public <R> Wrap notIn(SFunction<R, ?> column, Collection<?> collValues) {
        return not().in(column, collValues);
    }

    @Override
    public <R> Wrap in(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return addSubCondition(column, SqlKeyword.IN, func);
    }

    @Override
    public <R> Wrap notIn(SFunction<R, ?> column, Function<Wrap, Wrap> func) {
        return not().in(column, func);
    }

    @Override
    public <R> Wrap isNull(SFunction<R, ?> column) {
        return doIt(SqlKeyword.WHERE, columnToString(column), SqlKeyword.IS_NULL.toString());
    }

    @Override
    public <R> Wrap isNotNull(SFunction<R, ?> column) {
        return doIt(SqlKeyword.WHERE, columnToString(column), SqlKeyword.IS_NOT_NULL.toString());
    }

    @Override
    public Wrap not(Function<Wrap, Wrap> func) {
        return not().addLinkCondition(func);
    }

    @Override
    public Wrap or(Function<Wrap, Wrap> func) {
        return or().addLinkCondition(func);
    }

    /**
     * OR 嵌套
     * <p>
     * 例: orNest(i -> i.eq("name", "李白").ne("status", "活着"), e-> e.eq("id", 1).lt("num", 2))
     * 等价于 (( name = '李白' and status != '活着' ) or ( id = 1 and num < 2))
     * </p>
     *
     * @param functions 函数
     * @return wrap
     */
    @Override
    public Wrap orNest(Function<Wrap, Wrap>... functions) {
        List<String> sqlList = new ArrayList<>();
        for (Function<Wrap, Wrap> func : functions) {
            Wrap wrap = getWrap(func);
            String sql = wrap.expression.getWhereSegment();
            sqlList.add(String.format("(%s)", sql));
        }

        String sqlSegment = sqlList.stream().collect(joining(StringPool.SPACE + SqlKeyword.OR.toString() + StringPool.SPACE));
        return doIt(SqlKeyword.WHERE, String.format("(%s)", sqlSegment));
    }

    @Override
    public Wrap and(Function<Wrap, Wrap> func) {
        return and().addLinkCondition(func);
    }

    /**
     * AND 嵌套
     * <p>
     * 例: andNest(i -> i.eq("name", "李白").or().ne("status", "活着"), e-> e.eq("id", 1).lt("num", 2))
     * 等价于 (( name = '李白' or status != '活着' ) and ( id = 1 and num < 2))
     * </p>
     *
     * @param functions 函数
     * @return wrap
     */
    @Override
    public Wrap andNest(Function<Wrap, Wrap>... functions) {
        List<String> sqlList = new ArrayList<>();
        for (Function<Wrap, Wrap> func : functions) {
            Wrap wrap = getWrap(func);
            String sql = wrap.expression.getWhereSegment();
            sqlList.add(String.format("(%s)", sql));
        }

        String sqlSegment = sqlList.stream().collect(joining(StringPool.SPACE + SqlKeyword.AND.toString() + StringPool.SPACE));
        return doIt(SqlKeyword.WHERE, String.format("(%s)", sqlSegment));
    }

    @Override
    public Wrap link(Function<Wrap, Wrap> func) {
        return addLinkCondition(func);
    }

    @Override
    public <R, R2> Wrap on(SFunction<R, ?> r1, SFunction<R2, ?> r2) {
        String left = columnToString(r1);
        String tableLeft = StringUtil.split(left, StringPool.DOT)[0];
        String right = columnToString(r2);
        String tableRight = StringUtil.split(right, StringPool.DOT)[0];
        return doIt(SqlKeyword.WHERE, left, SqlKeyword.EQ.toString(), right);
    }

    @Override
    public Wrap exists(Function<Wrap, Wrap> func) {
        Wrap wrap = getWrap(func);
        return doIt(SqlKeyword.WHERE, SqlKeyword.EXISTS.toString(), String.format("(%s)", wrap.getAll()));
    }

    @Override
    public Wrap notExists(Function<Wrap, Wrap> func) {
        return not().exists(func);
    }

    @Override
    public Wrap connect(String sql, Object... value) {
        return doIt(SqlKeyword.WHERE, formatSql(sql, value));
    }

    @Override
    public String getSelect() {
        return expression.getSelect();
    }

    @Override
    public String getSelectSegment() {
        return expression.getSelectSegment();
    }

    @Override
    public String getFrom() {
        return expression.getFrom();
    }

    @Override
    public String getFromSegment() {
        return expression.getFromSegment();
    }

    @Override
    public String getWhere() {
        return expression.getWhere();
    }

    @Override
    public String getWhereSegment() {
        return expression.getWhereSegment();
    }

    @Override
    public String getCountWhere() {
        return expression.getCountWhere();
    }

    /**
     * 获取查询count的where条件， 不带 where
     *
     * @return
     */
    @Override
    public String getCountWhereSegment() {
        return expression.getCountWhereSegment();
    }

    @Override
    public String getGroupBy() {
        return expression.getGroupBy();
    }

    @Override
    public String getGroupBySegment() {
        return expression.getGroupBySegment();
    }

    @Override
    public String getHaving() {
        return expression.getHaving();
    }

    @Override
    public String getHavingSegment() {
        return expression.getHavingSegment();
    }

    @Override
    public String getOrderBy() {
        return expression.getOrderBy();
    }

    @Override
    public String getOrderBySegment() {
        return expression.getOrderBySegment();
    }

    @Override
    public String getAll() {
        return expression.getAll();
    }

    @Override
    public boolean isEmptyOfWhere() {
        return expression.getWhereList().isEmpty();
    }

    @Override
    public String getExecSql() {
        return toExecSql(getAll());
    }

    @Override
    public String getExecSqlFormat() {
        String sql = getExecSql();
        return SqlUtils.sqlFormat(sql);
    }

    @Override
    public void clear() {
        initNeed();
    }

    @Override
    public void clearSelect() {
        expression.clearSelect();
    }

    @Override
    public void clearFrom() {
        expression.clearFrom();
    }

    @Override
    public void clearWhere() {
        expression.clearWhere();
    }

    @Override
    public void clearGroupBy() {
        expression.clearGroupBy();
    }

    @Override
    public void clearHaving() {
        expression.clearHaving();
    }

    @Override
    public void clearOrderBy() {
        expression.clearOrderBy();
    }

    @Override
    public void clearUnion() {
        expression.clearUnion();
    }

    @Override
    public String getCount() {
        return expression.getCount();
    }

    @Override
    public String getSelectSql() {
        return toMybatisSql(getSelect());
    }

    @Override
    public String getFromSql() {
        return toMybatisSql(getFrom());
    }

    @Override
    public String getWhereSql() {
        return toMybatisSql(getWhere());
    }

    @Override
    public String getWhereSqlSegment() {
        return toMybatisSql(getWhereSegment());
    }

    @Override
    public String getGroupBySql() {
        return toMybatisSql(getGroupBy());
    }

    @Override
    public String getHavingSql() {
        return toMybatisSql(getHaving());
    }

    @Override
    public String getOrderBySql() {
        return toMybatisSql(getOrderBy());
    }

    @Override
    public String getAllSql() {
        return toMybatisSql(getAll());
    }

    @Override
    public String getCountSql() {
        return toMybatisSql(getCount());
    }

}
